﻿using AutoMapper;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq;
using Volo.Abp.Uow;
using YaAppoint.Appointments.Pools;
using YaAppoint.CustomSettings;
using YaAppoint.Devices;
using YaAppoint.ExamAppies;
using YaAppoint.ExamApplications;
using YaAppoint.Response;
using YaAppoint.Rules;

namespace YaAppoint.Appointments.AutoAppoint
{
    [RemoteService(isEnabled: false)]
    public class AutoAppointAppService(IExamApplyAppService examApplyAppService,
                                       IRepository<Device, Guid> deviceRepository,
                                       IRepository<DeviceType, Guid> deviceTypeRepository,
                                       IRepository<Pool, Guid> poolRepository,
                                       IPoolAppService poolAppService,
                                       IAppointPlanAppService appointPlanAppService,
                                       ICustomSettingAppService customSettingService,
                                       IAppointmentAppService appointmentAppService,
                                       AsyncQueryableExecuter queryableExecuter,
                                       IMapper mapper,
                                       IUnitOfWorkManager unitOfWorkManager,
                                       ISchemaFactory schemeFactory) : ApplicationService, IAutoAppointAppService, ITransientDependency
    {
        private readonly IExamApplyAppService _examApplyAppService = examApplyAppService;
        private readonly IRepository<Device, Guid> _deviceRepository = deviceRepository;
        private readonly IRepository<DeviceType, Guid> _deviceTypeRepository = deviceTypeRepository;
        private readonly IRepository<Pool, Guid> _poolRepository = poolRepository;
        private readonly ICustomSettingAppService _customSettingService = customSettingService;
        private readonly IMapper _mapper = mapper;
        private readonly AsyncQueryableExecuter _queryableExecuter = queryableExecuter;
        private readonly ISchemaFactory _schemeFactory = schemeFactory;
        private readonly IPoolAppService _poolAppService = poolAppService;
        private readonly IUnitOfWorkManager _unitOfWorkManager = unitOfWorkManager;
        private readonly IAppointmentAppService _appointmentAppService = appointmentAppService;
        private readonly IAppointPlanAppService _appointPlanAppService = appointPlanAppService;

        /// <summary>
        /// 自动预约
        /// </summary>
        public async Task<BackJson> Appoint(string applyNumbers,bool needPage)
        {
            var res = new BackJson();
            var applyExamItems = await _examApplyAppService.GetApplyItemListAsync(applyNumbers);
            if (applyExamItems == null || applyExamItems.Count == 0)
            {
                res.code = 1;
                res.msg = "申请单下无任何申请项目";
            }  
            else
            {
                if (!CheckWhetherSamePatient(applyExamItems))
                {
                    res.code = 3;// 申请单不属于同一患者
                    res.msg = "不是同一患者的申请单";
                    return res;
                }
                // 只预约申请单中还没有预约过的
                var unappointExamItems = applyExamItems.Where(ae => ae.AppointmentId == null).ToList();
                if (unappointExamItems == null || unappointExamItems.Count == 0)
                {
                    res.code = 1;
                    res.msg = "提供的申请单列表中所有申请项目已经被预约，无新项目需要预约";
                }
                else
                {
                    try
                    {
                       
                        res.code = 0;
                        res.data = await GenerateSolution(unappointExamItems, needPage);
                    }
                    catch (Exception ex)
                    {
                        res.code = 2;
                        res.msg = ex.Message;
                    }
                }
            }
            return res;
        }

        /// <summary>
        /// 检查是否为同一患者的申请单
        /// (同步方法，只有内存操作)
        /// </summary>
        public bool CheckWhetherSamePatient(List<ExamApplicationDto> list)
        {
            string current = null;
            bool res = true;
            foreach(var applyItem in list)
            {
                if (current == null)
                {
                    current = applyItem.PatientId;
                }
                else
                {
                    if (current != applyItem.PatientId)
                    {
                        res = false;
                        break;
                    }
                }
            }
            return res;
        }

        /// <summary>
        /// 获取推荐号池
        /// (用于his预约页面)
        /// (调用前，请确保applyItems为统一申请单号、且同一设备类型)
        /// 如果需要拆分，则返回
        /// </summary>
        /// <returns></returns>
        public async Task<List<PoolDto>> GetSuggestedPools(List<ExamApplicationDto> applyItems, DateTime date)
        {
            List<PoolDto> pools = new List<PoolDto>();
            var deviceTypeIdList = applyItems.DistinctBy(item => item.DeviceTypeId).ToList();
            if (applyItems.DistinctBy(item => item.DeviceTypeId).ToList().Count > 1)
            {
                throw new BusinessException(CustomErrorCodes.MoreThanOneDeviceTypeFoundInApplyItems);
            }
            var deviceTypeId = applyItems[0].DeviceTypeId;
            var timeFilter = _appointPlanAppService.GenerateTimeIntervalFilter(applyItems);
            if (!timeFilter.IsConflicting)
            {
                // (第一次访问数据库)
                var deviceList = await _deviceRepository.GetListAsync(d => d.DeviceTypeId == deviceTypeId);// 所有科室的设备
                // (纯内存操作)
                var totalDegree = _appointPlanAppService.CalculateDeviceSupportDegree(applyItems, _mapper.Map<List<Device>, List<DeviceDto>>(deviceList));
                // timeFiler 与 totalDegree 同步基准项目
                timeFilter.BaseLineApplyItem = totalDegree.BaseLineApplyItem;
                // 统计设备对申请项目的支持情况
                if (totalDegree.AllSupporteds != null && totalDegree.AllSupporteds.Count > 0)
                {
                    var devices = totalDegree.AllSupporteds.Select(degree => degree.Device).ToList();

                    // 第二次访问数据库
                    List<CustomSettingDto> settingList = await _customSettingService.GetDeviceSettingList(devices);

                    // 一次性检测和创建所有支持设备的所有号池计划对应的所有号池
                    await _poolAppService.CreateAllDevicesAllPools(devices, date);

                    var deviceIds = devices.Select(d => d.Id).ToList();

                    // 获取所有设备的所有可用号池，用户后面的规则过滤
                    var allPools = await _poolRepository.GetListAsync(p => deviceIds.Contains(p.DeviceId) && p.Date.Date == date.Date);

                    if (devices.Count == 0) // 设备类型下无设备
                    {
                        return pools;// 此时pools元素个数为0
                    }
                    else
                    {
                        // 批量获取设备的ruleSchema
                        var deviceSchemaList = await _schemeFactory.GetRuleSchemaList(devices);

                        foreach (var device in devices)
                        {
                            var schema = deviceSchemaList.First(s => s.DeviceId == device.Id);
                            if (schema == null)
                            {
                                continue;
                            }
                            // 生成过滤结构
                            var filterSchema = _schemeFactory.GetDeviceFilerSchema(schema);
                            CustomSettingDto? setting = settingList.FirstOrDefault(s => s.DepartmentId == device.DepartmentId
                                                                                       && s.DeviceTypeId == device.DeviceTypeId
                                                                                       && s.DeviceId == device.Id
                                                                                   );
                            if (setting == null)
                            {
                                Logger.LogError($"未获取到设备:{device.Name}的设置！不推荐此设备的好吃！");
                                continue;
                            }

                            //设备当天所有的没过时的号池
                            var devicePools = allPools.Where(p => p.DeviceId == device.Id
                                                            && p.Date.Date == date.Date
                                                            && DateTime.Now < p.StartTime.AddMinutes(-(double)setting.LatestAppoint!)
                                                            && p.IsActive).ToList();
                            foreach (var pool in devicePools)
                            {
                                //对pool执行过滤结构，生成过滤结果(是否通过，通过了的还包含优先级)\
                                var poolDto = _mapper.Map<Pool, PoolDto>(pool);
                                var result = _schemeFactory.ExecuteFilterSchema(poolDto, applyItems, filterSchema);
                                if (result.IsValid)
                                {
                                    poolDto.SuggestOrder = result.SuggestOrder;
                                    pools.Add(poolDto);
                                }
                            }
                        }
                        return pools.OrderByDescending(p => p.SuggestOrder).ToList();
                    }
                }
            }
            else
            {
                throw new BusinessException(CustomErrorCodes.ExamItemConflictExist);
            }

            return pools;
        }


        

        /// <summary>
        /// 获取推荐方案
        /// </summary>
        public async Task<AppointResult> GenerateSolution( List<ExamApplicationDto> applyItems,bool needPage)
        {
            var res = new AppointResult();
            // 预约计划集合
            List<AppointPlan> allAppointPlans = new List<AppointPlan>();
            var deviceTypeList = applyItems.DistinctBy(i => i.DeviceTypeId).Select(i => i.DeviceTypeId).ToList();
            var allDevices = await _deviceRepository.GetListAsync(d=> deviceTypeList.Contains(d.DeviceTypeId));
            var globalSetting = await _customSettingService.GetGlobalSetting();
            // 区分设备类型进行划分预约计划
            foreach (var dtId in deviceTypeList)
            {
                var deviceList = allDevices.Where(d => d.DeviceTypeId == dtId).ToList();
                // 当前设备类型下的检查项目
                var dtApplyItems = applyItems.Where(i => i.DeviceTypeId == dtId).OrderByDescending(i => i.ExamItem.Order).ToList();
                // 计算出整体设备支持情况
                var degreeFilter = _appointPlanAppService.CalculateDeviceSupportDegree(dtApplyItems, _mapper.Map<List<Device>,List<DeviceDto>>(deviceList));
                // 根据设备支持情况、申请项目，生成该设备类型下的项目的预约计划
                var dtAppointPlans = await _appointPlanAppService.GenerateAppointPlans(degreeFilter, dtApplyItems,globalSetting);
                allAppointPlans.AddRange(dtAppointPlans); // 添加该设备类型下的预约计划
            }
            // 先按有无时间段要求进行排序(有时间要求在前)，再按时间段开始时间进行排序，先开始的先预约
            allAppointPlans = allAppointPlans.OrderBy(p => p.StartTime == null ? 1 : 0).ThenBy(p => p.StartTime).ToList();
            List<AppointmentDto> appointments = new List<AppointmentDto>();
            
            // 存在推荐号池的预约计划
            var suggestPoolsPlans = allAppointPlans.Where(plan => plan.SuggestedPools.Count > 0)
                                                  .OrderBy(plan => plan.SuggestedPools.MinBy(pool => pool.StartTime)!.StartTime).ToList();
            // 无推荐号池的预约计划
            var noSuggestPoolsPlans = allAppointPlans.Where(plan => plan.SuggestedPools.Count == 0).ToList();
            var noSuggestPoolsApplyItems = new List<ExamApplicationDto>();
            foreach (var plan in noSuggestPoolsPlans)
            {
                noSuggestPoolsApplyItems.AddRange(plan.ApplyItems);
            }
            // 对有推荐号池的预约计划进行预约
            foreach (var plan in suggestPoolsPlans)
            {
                AppointmentStatus status = new AppointmentStatus();
                if (needPage)
                {
                    status = AppointmentStatus.Unconfirmed;
                }
                else
                {
                    status = AppointmentStatus.Submit;
                }
                if (appointments.Count == 0)
                {
                    var inSameIntervalPools = plan.SuggestedPools.Where(p =>Convert.ToDateTime(p.StartTime) ==Convert.ToDateTime(plan.SuggestedPools[0].StartTime)
                                                                   &&Convert.ToDateTime(p.EndTime) ==Convert.ToDateTime(plan.SuggestedPools[0].EndTime))
                                                       .OrderBy(p => p.CurrentCount)
                                                       .ToList();
                    var appointment = await _appointmentAppService.CreateAppointment(plan.ApplyItems, inSameIntervalPools[0],status);
                    appointments.Add(appointment);
                }
                else
                {
                        // 与上一个预约时间段的结束时间间隔30分钟的号池     
                    plan.SuggestedPools = plan.SuggestedPools
                        .Where(p => Convert.ToDateTime(p.StartTime) >= Convert.ToDateTime(appointments[appointments.Count - 1].Pool.EndTime).AddMinutes(30))
                        .ToList();
                    var insameIntetvalPools = plan.SuggestedPools
                        .Where(p => Convert.ToDateTime(p.StartTime) == Convert.ToDateTime(plan.SuggestedPools[0].StartTime)&& Convert.ToDateTime(p.EndTime) == Convert.ToDateTime(plan.SuggestedPools[0].EndTime))
                        .ToList();
                    var appointment = await _appointmentAppService.CreateAppointment(plan.ApplyItems, insameIntetvalPools[0],status);
                    appointments.Add(appointment);
                }
            }

            // 是否需要回写his
            if (!needPage)
            {
                try
                {
                    List<ExamApplicationDto> list = new List<ExamApplicationDto>();
                    foreach (var appoint in appointments)
                    {
                        foreach (var item in appoint.ExamApplications)
                        {
                            list.Add(item);
                        }
                    }

                    // 将检查项目预约状态回写给his
                    List<ApplyExamItemStatusChangeDto> changeDtos = new List<ApplyExamItemStatusChangeDto>();
                    foreach (var applyItem in list)
                    {
                        var dto = new ApplyExamItemStatusChangeDto();
                        dto.Name = applyItem.Name;
                        dto.PatientId = applyItem.PatientId;
                        dto.AppointmentInfo = applyItem.AppointmentInfo;
                        dto.ApplyNumber = applyItem.ApplyNumber;
                        dto.ChangeType = ApplyExamItemStatusChangeType.Submit;
                        dto.ChangeTypeStr = "新建预约";
                        dto.ApplyNumber = applyItem.ApplyNumber;
                        dto.ExamItemNumber = applyItem.ExamItemNumber;
                        dto.ExamItemCode = applyItem.ExamItemCode;
                        dto.ExamItemName = applyItem.ExamItemName;

                        changeDtos.Add(dto);
                    }
                    await _appointmentAppService.ApplyExamItemStatusChangeToHis(changeDtos);
                }
                catch (Exception e)
                {
                    throw new Exception(e.Message);
                }
            }

            res.Appointments = appointments;
            res.NoSuggestPoolsApplyItems = noSuggestPoolsApplyItems;
            // 调用决策服务，  
            return res;
        }

        /// <summary>
        /// 获取申请项目列表
        /// (自动预约创建后，此时AppointmentStatus 为 UnConfirmed ,等待医生的确认)
        /// </summary>
        /// <param name="applyNumberList"></param>
        /// <returns></returns>
        public async Task<BackJson> GetApplyItems(string applyNumbers)
        {
            var res = new BackJson();
            if (string.IsNullOrEmpty(applyNumbers))
            {
                res.code = 1;
                res.msg = "申请项目参数：applyNumbers不能为空！";
            }
            else
            {
                List<ExamApplicationDto> applyItems = await _examApplyAppService.GetApplyItemListAsync(applyNumbers);
                res.code = 0;
                res.data = applyItems.OrderBy(item => item.AppointmentId).ToList();
            }
            return res;
        }
    }
}
